﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Configuration;
using System.Reflection;

using WcfCore;

namespace WcfClientHost
{
	class Program
	{
		public static string clientMachineName;
		public static string serviceMachineName;
		private static string remoteUser;
		private static string remotePassword;
		private static string wcfServerDir;
		private static string wcfServerExeName;
		private static string wcfServerExePath;
		private static string wcfServerConfigPath;
		private static string wcfServerBindingConfigPath;
		private static string wcfRemoteServerDir;
		private static string wcfRemoteServerExeName;
		private static string wcfRemoteServerExePath;
		private static string wcfRemoteServerConfigPath;
		private static string wcfRemoteServerBindingConfigPath;
		private static string wcfClientDir;
		private static string wcfClientExeName;
		private static string wcfClientExePath;
		private static string wcfClientConfigPath;
		private static string wcfClientBindingConfigPath;
		private static string logmanExePath;
		private static string psexecExePath;
		private static string pskillExePath;
		private static string filename;

		static void Main(string[] args)
		{
			Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

			if (args.Length != 1)
			{
				Console.WriteLine("Gotta specify the input file.");
				throw new ArgumentException("Wrong command line arguments.");
			}

			filename = args[0];
			if (!File.Exists(filename))
			{
				Console.WriteLine("Gotta specify an existing file containing the test input information.");
				throw new ArgumentException("Wrong command line arguments.");
			}

			FileInfo fi = new FileInfo(filename);
			try
			{
				clientMachineName = ConfigurationManager.AppSettings["ClientMachineName"];
				serviceMachineName = ConfigurationManager.AppSettings["ServiceMachineName"];
				remoteUser = ConfigurationManager.AppSettings["RemoteUser"];
				remotePassword = ConfigurationManager.AppSettings["RemotePassword"];
				wcfServerDir = ConfigurationManager.AppSettings["WcfSvcHostDir"];
				wcfServerExeName = ConfigurationManager.AppSettings["WcfSvcHostExeName"];
				wcfServerExePath = Path.Combine(wcfServerDir, wcfServerExeName);
				wcfServerConfigPath = wcfServerExePath + ".config";
				wcfServerBindingConfigPath = Path.Combine(wcfServerDir, "Binding.config");
				wcfRemoteServerDir = ConfigurationManager.AppSettings["WcfRemoteSvcHostDir"];
				wcfRemoteServerExeName = ConfigurationManager.AppSettings["WcfRemoteSvcHostExeName"];
				wcfRemoteServerExePath = Path.Combine(wcfRemoteServerDir, wcfRemoteServerExeName);
				wcfRemoteServerConfigPath = wcfServerExePath + ".config";
				wcfRemoteServerBindingConfigPath = Path.Combine(wcfServerDir, "Bindings.Default.config");
				wcfClientDir = ConfigurationManager.AppSettings["WcfClientHostDir"];
				wcfClientExeName = ConfigurationManager.AppSettings["WcfClientHostExeName"];
				wcfClientExePath = Path.Combine(wcfClientDir, wcfClientExeName);
				wcfClientConfigPath = wcfClientExePath + ".config";
				wcfClientBindingConfigPath = Path.Combine(wcfClientDir, "Bindings.Default.config");
				logmanExePath = ConfigurationManager.AppSettings["LogManExePath"];
				psexecExePath = ConfigurationManager.AppSettings["PsExecExePath"];
				pskillExePath = ConfigurationManager.AppSettings["PsKillExePath"];

				WcfTestInput input = WcfTestInput.Deserialize(fi.FullName);
				using (PerformanceTestWrapper wrapper = new PerformanceTestWrapper(input))
				{
					WcfLogger.Log(string.Format("Starting test {0}.", input.DisplayName));
					DetermineScopeAndPerformTest(input);
				}
			}
			catch (Exception exc)
			{
				string log = exc.ToString();
				Exception inner = exc.InnerException;
				while (inner != null)
				{
					log += inner.ToString();
					inner = inner.InnerException;
				}
				WcfLogger.Log(exc.ToString());
			}
		}

		private static void DetermineScopeAndPerformTest(WcfTestInput input)
		{
			switch (input.Scope)
			{
				case PerformanceTestScope.Direct:
					PerformDirect(input);
					break;
				case PerformanceTestScope.SameAppDomain:
					PerformSameAppDomain(input);
					break;
				case PerformanceTestScope.SameProcess:
					PerformSameProcess(input);
					break;
				case PerformanceTestScope.SameMachine:
					PerformSameMachine(input);
					break;
				case PerformanceTestScope.Remote:
					PerformRemote(input);
					break;
			}
		}

		/// <summary>
		/// Tests within AppDomains
		/// </summary>
		static void PerformDirect(WcfTestInput input)
		{
			PerformTest(input);
		}

		static void PerformSameAppDomain(WcfTestInput input)
		{
			PerformSameProcess(input);
		}

		/// <summary>
		/// Tests seperate AppDomains within one process
		/// </summary>
		static void PerformSameProcess(WcfTestInput input)
		{
			string configFile = string.Format("{0}.{1}", Assembly.GetExecutingAssembly().Location, "config");
			using (WcfServiceHostWrapper serverWrapper = new WcfServiceHostWrapper(input))
			{
				PerformTest(input);
			}
		}

		static void PerformSameMachine(WcfTestInput input)
		{
			if (File.Exists(wcfServerConfigPath))
			{
				File.SetAttributes(wcfServerConfigPath, FileAttributes.Normal);
			}
			File.Copy(input.ConfigFileFullPath, wcfServerConfigPath, true);

			//if (File.Exists(wcfServerBindingConfigPath))
			//{
			//    File.SetAttributes(wcfServerBindingConfigPath, FileAttributes.Normal);
			//}
			//File.Copy(input.BindingConfigFileFullPath, wcfServerBindingConfigPath, true);

			HostInfo hostInfo = new HostInfo(filename, WcfTestInput.ServiceMachineName, wcfServerExePath);
			using (HostWrapper wrapper = new HostWrapper(hostInfo, psexecExePath, pskillExePath))
			{
				PerformTest(input);
			}
		}

		static void PerformRemote(WcfTestInput input)
		{
			if (File.Exists(wcfRemoteServerConfigPath))
			{
				File.SetAttributes(wcfRemoteServerConfigPath, FileAttributes.Normal);
			}
			File.Copy(input.ConfigFileFullPath, wcfRemoteServerConfigPath, true);

			//if (File.Exists(wcfRemoteServerBindingConfigPath))
			//{
			//    File.SetAttributes(wcfRemoteServerBindingConfigPath, FileAttributes.Normal);
			//}
			//File.Copy(input.BindingConfigFileFullPath, wcfRemoteServerBindingConfigPath, true);

			HostInfo hostInfo = new HostInfo(filename, WcfTestInput.ServiceMachineName, wcfServerExePath, remoteUser, remotePassword);
			using (HostWrapper wrapper = new HostWrapper(hostInfo, psexecExePath, pskillExePath))
			{
				List<PerformanceCounterLogInfo> infos = new List<PerformanceCounterLogInfo>(2);
				infos.Add(CreateClientPerformanceCounterLogInfo(input));
				infos.Add(CreateRemoteServerPerformanceCounterLogInfo(input));

				PerformTest(input, infos.ToArray());
			}
		}

		private static PerformanceCounterLogInfo CreateClientPerformanceCounterLogInfo(WcfTestInput input)
		{
			PerformanceCounterLogInfo info = new PerformanceCounterLogInfo();
			info.LogIntervalInSeconds = WcfTestInput.PerformanceCounterLogInterval;
			info.LogManExePath = logmanExePath;
			info.MachineName = WcfTestInput.ClientMachineName;
			info.Name = input.Name;
			info.OutputFilename = input.PerformanceCounterOutputFilepath;
			info.PerformanceCounterFilename = input.PerformanceCounterFileFullPath;
			info.PsExecExePath = psexecExePath;
			return info;
		}

		private static PerformanceCounterLogInfo CreateRemoteServerPerformanceCounterLogInfo(WcfTestInput input)
		{
			PerformanceCounterLogInfo info = new PerformanceCounterLogInfo();
			info.LogIntervalInSeconds = WcfTestInput.PerformanceCounterLogInterval;
			info.LogManExePath = logmanExePath;
			info.MachineName = WcfTestInput.ServiceMachineName;
			info.Name = input.Name;
			info.OutputFilename = input.PerformanceCounterOutputFilepath;
			info.PerformanceCounterFilename = WcfTestInput.RemotePerformanceCounterFileFullName;
			info.PsExecExePath = psexecExePath;
			//info.Username = remoteUser;
			//info.Password = remotePassword;
			return info;
		}

		private static void PerformTest(WcfTestInput input)
		{
			PerformTest(input, new PerformanceCounterLogInfo[] { CreateClientPerformanceCounterLogInfo(input) });
		}

		private static void PerformTest(WcfTestInput input, PerformanceCounterLogInfo[] infos)
		{
			WcfTestOutput output = null;
			using (PerformanceCounterLog log = new PerformanceCounterLog(infos))
			{
				using (WcfPerformanceTestService test = new WcfPerformanceTestService(input))
				{
					output = test.DoPerformanceTest();

					if (WcfTestInput.TestCleanupTimeMSecs > 0)
					{
						Thread.Sleep(WcfTestInput.TestCleanupTimeMSecs);
					}
				}
			}
		}
	}
}
